<?php

class    PacketID
{
    const   MIN_PACKET_ID = 1;
    const   MAX_PACKET_ID = 65535;
    private static $packet_id_current = self::MAX_PACKET_ID;
    
    public static function next()
    {
        if (self::MAX_PACKET_ID == self::$packet_id_current) {
            self::$packet_id_current = self::MIN_PACKET_ID;
        }
        else {
            self::$packet_id_current += 1;
        } 
        
        return self::$packet_id_current;
    } 
}    // class PacketID 


class    Packet
{
    const REQUEST  = 'REQUEST';
    const RESPONSE = 'RESPONSE';
    
    const OP_APPEND  = 'APPEND';
    const OP_INSERT  = 'INSERT';
    const OP_UPDATE  = 'UPDATE';
    const OP_REPLACE = 'REPLACE';
    const OP_ERASE   = 'ERASE';
    const OP_FIND    = 'FIND';

    RC_OK        = 200
    RC_ERROR     = 300
    RC_BAD_ROUTE = 400


    public $action;    // REQUEST/RESPONSE
    public $table;     // table name 
    public $op;        // operation
    public $key;       // key of record
    public $data;      // data of record
    public $error;     // error text
    public $state;     // state code
    public $packid;    // id of packet 
    public $respid;    // which one this packet response  
    
    public function __construct()
    {
        $this->action = NULL;
        $this->table  = NULL;
        $this->op     = NULL;
        $this->key    = NULL;
        $this->data   = NULL;
        $this->error  = NULL;
        $this->state  = 0;
        $this->packid = 0;
        $this->respid = 0;
    }
    
    public function setRequest()
    {
        if (self::REQUEST != $this->action) {
            $this->action = self::REQUEST;
        } 
    }
    public function setResponse()
    {
        if (self::RESPONSE != $this->action) {
            $this->action = self::RESPONSE;
        } 
    }
    public function isRequest()
    {
        return (self::REQUEST == $this->action);
    }
    public function isResponse()
    {
        return (self::RESPONSE == $this->action);
    }
    
    public function isComplete() 
    {
        if (empty($this->action) || empty($this->table) || empty($this->op) || empty($this->key)) {
            return false;
        }
        
        return true;
    }
    
    function    depack($record)
    {
        $field_separator = "\t";
        $kv_separator = ':';
        $fields = split($field_separator, $record);
        foreach ($fields as $field) {
            list($k, $v) = split($kv_separator, $field, 2);
            if (empty($v)) {
                continue;
            }
            switch($k) {
            case 'action' :
                $this->action = $v;
                break;
            case 'table' :
                $this->table = $v;
                break;
            case 'op' :
                $this->op = $v;
                break;
            case 'key' :
                $this->key = base64_decode($v);
                break;
            case 'data' :
                $this->data = base64_decode($v);
                break;
            case 'error' :
                $this->error = base64_decode($v);
                break;
            case 'state' :
                $this->state = intval($v);
                break;
            case 'packid' :
                $this->packid = intval($v);
                break;
            case 'respid' :
                $this->respid = intval($v);
                break;
            }
        }
        if (!$this->isComplete()) {
            return false;
        }
        
        return $this;
    }    // function depack()
    
    function    enpack()
    {
        $packid = $this->packid;
        if (empty($packid)) {
            $packid = PacketID::next();
        }
        
        $fields = array();
        $fields[] = 'action:' . $this->action;
        $fields[] = 'table:' . $this->table;
        $fields[] = 'op:' . $this->op;
        $fields[] = 'key:' . base64_encode($this->key);
        $fields[] = 'data:' . base64_encode($this->data);
        $fields[] = 'error:' . base64_encode($this->error);
        $fields[] = 'state:' . $state;
        $fields[] = 'packid:' . $packid;
        if ($this->respid > 0) {
            $fields[] = 'respid:' . $this->respid;
        }
        
        $record = join("\t", $fields);
        return array($record, $packid);
    }    // function enpack()

}    // class Packet

function    depack($record)
{
    $packet = new Packet();
    
    $ok = $packet->depack($record);
    if (!$ok) {
        return false;
    }

    return $packet;
}    // function depack()

function    enpack(&$packet)
{
    return $packet->enpack();
}    // function enpack()

